import React from 'react'
import { View, Text, StyleSheet, TouchableHighlight, TouchableOpacity } from 'react-native'
import dayjs, { Dayjs } from 'dayjs'

interface Props{
  value?: Date | string | number | null;
  dayRender?: (day?: Date | Dayjs | string | number | null, value?: Date | Dayjs | string | number | null) => JSX.Element;
  onChange?: (day: Date | Dayjs | string | number | null) => void;
}
// declare DayItem = Date | string | number | null
interface State{
  value: Date | string | number | null;
  month: number;
  year: number;
  dayList?: (null | Dayjs)[][];
}

export default class Calendar extends React.Component<Props, State> {
  public weekDayList: string[] = ['日', '一', '二', '三', '四', '五', '六']

  public daysForMonth: number[] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

  public constructor (props: Props) {
    super(props)
    const value = this.props.value ? this.props.value : dayjs()
    this.state = {
      value: this.props.value ? this.props.value : null,
      month: dayjs(value).month(),
      year: dayjs(value).year()
    }
  }

  public componentDidUpdate (preProps: Props): void {
    if (preProps.value !== this.props.value) {
      // this.setState({
      //   value: this.props.value
      // })
    }
  }

  public componentWillMount (): void {
    this.initMonthDayList()
  }

  public getDaysForMonth (month: number = this.state.month, year: number = this.state.year): number {
    let daysForMonth = this.daysForMonth
    if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
      daysForMonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    }
    return daysForMonth[month]
  }

  public initMonthDayList (month: number = this.state.month, year: number = this.state.year): void {
    const firstDay = dayjs().set('year', year).set('month', month).set('date', 1)
    const dayOfFirstDay = firstDay.day()
    const dayList = []
    let firstRow = true
    for (let d = 1; d <= this.getDaysForMonth(month, year);) {
      const row = []
      for (let j = 0; j < 7; j++) {
        if ((firstRow && dayOfFirstDay > j) || d > this.getDaysForMonth(month, year)) {
          row.push(null)
          continue
        }
        row.push(firstDay.set('date', d++))
      }
      dayList.push(row)
      firstRow = false
    }
    this.setState({ dayList })
  }

  public preMonth (): void {
    const { month, year } = this.state
    const day = dayjs().set('y', year).set('month', month).subtract(1, 'month')
    this.reInitDay(day)
  }

  public nextMonth (): void {
    const { month, year } = this.state
    const day = dayjs().set('y', year).set('month', month).add(1, 'month')
    this.reInitDay(day)
  }

  public preYear (): void {
    const { month, year } = this.state
    const day = dayjs().set('y', year).set('month', month).subtract(1, 'year')
    this.reInitDay(day)
  }

  public nextYear (): void {
    const { month, year } = this.state
    const day = dayjs().set('y', year).set('month', month).add(1, 'year')
    this.reInitDay(day)
  }

  public reInitDay (day: Dayjs): void {
    this.setState({
      month: day.month(),
      year: day.year()
    }, (): void => {
      this.initMonthDayList()
    })
  }

  public theSameDay (day1: Dayjs| Date | string | number | null, day2: Dayjs| Date | string | number | null): boolean {
    if (!day1 || !day2) return false
    return dayjs(day1).format('YYYY-MM-DD') === dayjs(day2).format('YYYY-MM-DD')
  }

  public onChange (item: Date | string | null): void {
    this.setState(
      {
        value: item
      }
    )
    this.props.onChange && this.props.onChange(item)
  }

  public renderDays (): JSX.Element[] | void {
    return this.state.dayList && this.state.dayList.map((row: (Dayjs | null)[], index: number): JSX.Element => {
      return (
        <View key={index} style={{ flexDirection: 'row' }}>
          {
            row.map((item: Dayjs | null, index: number): JSX.Element => {
              let day
              if (this.props.dayRender) {
                day = this.props.dayRender(item, this.props.value)
              } else {
                day = (<Text style={[{ textAlign: 'center' }, { color: this.theSameDay(this.props.value || null, item) ? 'red' : '#000' }]}>{item ? item.date() : ''}</Text>)
              }
              return (
                <TouchableOpacity style={{ flex: 1 }} key={index} onPress={(): void => { item && this.onChange(item.toDate()) }} >
                  {day}
                </TouchableOpacity>
              )
            })
          }
        </View>
      )
    })
  }

  public render (): JSX.Element {
    const { year, month } = this.state
    return (
      <View>
        <View style={styles.month_ctl}>
          <TouchableOpacity onPress={(): void => { this.preYear() }} style={{ marginRight: 5 }}>
            <Text style={styles.header_text}>{'《'}</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={(): void => { this.preMonth() }} style={{ marginRight: 5 }}>
            <Text style={styles.header_text}>{'<'}</Text>
          </TouchableOpacity>
          <Text style={styles.header_text}>{`${year}.${month + 1}`}</Text>
          <TouchableOpacity onPress={(): void => { this.nextMonth() }} style={{ marginLeft: 5 }}>
            <Text style={styles.header_text}>{'>'}</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={(): void => { this.nextYear() }} style={{ marginLeft: 5 }}>
            <Text style={styles.header_text}>{'》'}</Text>
          </TouchableOpacity>

        </View>
        <View style={styles.header}>
          { this.weekDayList.map((item, index): JSX.Element => {
            return (
              <Text key={index} style={[styles.item]}>{item}</Text>
            )
          })
          }
        </View>
        {this.renderDays()}
      </View>
    )
  }
}

const styles = StyleSheet.create({
  month_ctl: {
    flexDirection: 'row',
    justifyContent: 'center',
    marginBottom: 10
  },
  header_text: {
    fontSize: 20,
    fontWeight: 'bold'
  },
  header: {
    flexDirection: 'row',
    width: '100%'
  },
  item: {
    flex: 1,
    textAlign: 'center'
  }
})
